﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Xml.Linq;
using Accountant.DataModel;
using Accountant.Utils;
using Caliburn.Micro;
using System.Linq;

namespace Accountant.ViewModels
{
  [Export(typeof(IShell))]
  [Export(typeof(ShellViewModel))]
  [PartCreationPolicy(CreationPolicy.Shared)]
  public class ShellViewModel : Screen, IShell
  {
    public override string DisplayName { get { return "Профессиональная домашняя бухгалтерия®"; } set { } }

    [Import]
    public IWindowManager WindowManager { get; set; }
    [Import]
    public AccountantContext Data { get; set; }
    [Import]
    public OperationsSource OperationsSource { get; set; }

    public List<Account> RootAccounts
    {
      get
      {
        return Data.Accounts
          .Where(acc => acc.BaseAccount == null && !acc.IsHidden)
          .ToList();
      }
    }

    #region ' SelectedAccount '

    private Account _selectedAccount;

    public Account SelectedAccount
    {
      get { return _selectedAccount; }
      set
      {
        if (Equals(value, _selectedAccount)) return;
        _selectedAccount = value;
        NotifyOfPropertyChange(() => SelectedAccount);
        OperationsSource.Account = _selectedAccount;
      }
    }

    #endregion

    protected override void OnInitialize()
    {
      Data.Database.SetInitializer(new DbInitializer());
      Reload();
    }

    #region ' Menu Items '

    public void Update()
    {
      NotifyOfPropertyChange(() => RootAccounts);
      OperationsSource.Update();
    }
    public void ShowAllTransactions()
    {
      SelectedAccount = null;
    }
    public void GenerateRandomData()
    {
      using (var gen = new RandomDataGenerator(Data, (int)(DateTime.UtcNow.Ticks >> 32)))
        gen.Run(5000, DateTime.Now);
      Reload();
    }
    public void CleanDb()
    {
      Data.Database.Delete();
      Reload();
    }
    public void AddAccount()
    {
      var vm = new AddAccountViewModel(Data, SelectedAccount);
      if (WindowManager.ShowDialog(vm) != true) return;
      Data.Accounts.Add(vm.AccountUnderCreation);
      Data.SaveChanges();
    }
    public void AddCurrency()
    {
      WindowManager.ShowDialog(new AddCurrencyViewModel(Data));
    }
    public void AddStarting()
    {
      var vm = new AddOperationViewModel(Data, TransactionDirection.Starting);
      if (WindowManager.ShowDialog(vm) != true) return;
      Data.Transactions.Add(vm.TransactionUnderConstruction);
      Data.SaveChanges();
    }
    public void AddIncome()
    {
      var vm = new AddOperationViewModel(Data, TransactionDirection.Income);
      if (WindowManager.ShowDialog(vm) != true) return;
      Data.Transactions.Add(vm.TransactionUnderConstruction);
      Data.SaveChanges();
    }
    public void AddTransfer()
    {
      var vm = new AddOperationViewModel(Data, TransactionDirection.Transfer);
      if (WindowManager.ShowDialog(vm) != true) return;
      Data.Transactions.Add(vm.TransactionUnderConstruction);
      Data.SaveChanges();
    }
    public void AddExpense()
    {
      var vm = new AddOperationViewModel(Data, TransactionDirection.Expense);
      if (WindowManager.ShowDialog(vm) != true) return;
      Data.Transactions.Add(vm.TransactionUnderConstruction);
      Data.SaveChanges();
    }
    public void AddExchange()
    {
      var vm = new AddExchangeViewModel(Data);
      if (WindowManager.ShowDialog(vm) != true) return;
      Data.Transactions.Add(vm.FirstTransactionUnderCreation);
      Data.Transactions.Add(vm.SecondTransactionUnderCreation);
      Data.SaveChanges();
    }
    public void AddToAccount()
    {
      MessageBox.Show("Not implemented");
    }
    public void Reload()
    {
      Data.Load();
      Update();
    }
    public void LoadExchangeRatesFromNbrbSite()
    {
      var byr = Data.Currencies.Single(c => c.Name == "BYR");
      var needed = Data.Currencies.ToDictionary(c => c.Name, c => c);
      var toDate = DateTime.Now;
      var fromDate = DateTime.Now.AddDays(-7);

      var client = new WebClient();
      var exRatesRefStr = client.DownloadString(
      new Uri("http://nbrb.by/Services/XmlExRatesRef.aspx"));
      var exRatesRef = XDocument.Parse(exRatesRefStr);
      var x = from xml in exRatesRef.Element("DailyExRates").Elements("Currency")
              select new
              {
                Id = int.Parse(xml.Attribute("Id").Value),
                Name = xml.Element("CharCode").Value,
                Scale = decimal.Parse(xml.Element("Scale").Value)
              } 
              into curr where needed.ContainsKey(curr.Name)
              from node in GetExRatesDyn(client, curr.Id, fromDate, toDate)
              select new
              {
                Currency = needed[curr.Name],
                Date = DateTime.Parse(node.Attribute("Date").Value),
                Rate = decimal.Parse(node.Element("Rate").Value),
                Scale = curr.Scale,
              };

      foreach (var rate in x)
      {
        Data.ExchangeRates.Add(
          new ExchangeRate
            {
              StartAt = rate.Date,
              CurrencyFrom = byr,
              CurrencyTo = rate.Currency,
              Rate = rate.Rate,
              // 10000 - принятый способ записи бел рублей
              Scale = rate.Scale * 10000,
              Source = ExchangeRateSources.Nbrb,
            });
      }
      Data.SaveChanges();
    }
    public void OpenEditing()
    {
      WindowManager.ShowDialog(IoC.Get<EditingViewModel>());
    }

    private static IEnumerable<XElement> GetExRatesDyn(WebClient client, int curId, DateTime fromDate, DateTime toDate)
    {
      var doc = XDocument.Parse(client.DownloadString(new Uri(string.Format(
        "http://nbrb.by/Services/XmlExRatesDyn.aspx?curId={0}&fromDate={1}&toDate={2}",
        curId, fromDate.ToString("d"), toDate.ToString("d"))))).Element("Currency");
      return doc != null ? doc.Elements("Record") : Enumerable.Empty<XElement>();
    }

    #endregion

    public void ActivateAccount()
    {
      var clone = Clone(SelectedAccount);
      var vm = new AddAccountViewModel(clone, Data);
      vm.PossibleBase.Remove(SelectedAccount);
      if (WindowManager.ShowDialog(vm) != true) return;
      Copy(SelectedAccount, clone);
      Data.SaveChanges();
    }
    private static Account Clone(Account acc)
    {
      var res = new Account();
      Copy(res, acc);
      return res;
    }

    private static void Copy(Account dest, Account source)
    {
      dest.BaseAccount = source.BaseAccount;
      dest.Name = source.Name;
      dest.Comment = source.Comment;
      dest.IsAbstract = source.IsAbstract;
      dest.IsHidden = source.IsHidden;
      dest.IsSealed = source.IsSelected;
      dest.IsPrimaryCurrencyExclusive = source.IsPrimaryCurrencyExclusive;
      dest.PrimaryCurrency = source.PrimaryCurrency;
    }
  }
}
